package xyz.mvfast.excel;

import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import xyz.mvfast.annotations.Index;
import xyz.mvfast.util.DataConveter;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author fuzeqiang
 * @projectName easy-import
 * @title: ExcelUtil
 * @date 2021/5/24 16:46
 */
@Slf4j
public class ExcelUtil {


    /**
     * 默认使用GBK编码，解决Windows下默认GB2312的问题
     */
    private static final String DEFAULT_CHARSET_NAME = "GBK";


    /**
     * 默认一次读1000条数据
     */
    private static final int DEFAULT_BATCH_SIZE= 1000;


    /**
     * 默认第一行的行号
     */
    private static final int HEADER_LINE_NUMBER = 1;


    /**
     * 默认sheet页
     */
    private static final int DEFAULT_SHEET_NUMBER = 0;



    /**
     * 解析csv文件(分段读)
     * @param file
     * @param claz
     * @param <T>
     * @return
     */
    public <T> List<T> readEntityFromFileWithBatch(File file, Class<T> claz, int start, int batchSize){
        return readEntityFromFileWithBatch(file, claz, start, batchSize, DEFAULT_SHEET_NUMBER);
    }


    /**
     * 文件输出
     * @param out
     * @param header
     * @param list
     * @param clas
     */
    public <T> void writeEntityToFile(OutputStream out, String[] header, List<T> list, Class<T> clas, boolean isXlsx){
        //创建workbook
        Workbook workbook = null;
        try {
            if (isXlsx){
                workbook = new XSSFWorkbook();
            }else {
                workbook = new HSSFWorkbook();
            }
            //创建sheet页
            Sheet sheet = workbook.createSheet();
            if (header != null && header.length > 0){
                Row row = sheet.createRow(0);
                for (int i=0; i<header.length; i++){
                    Cell cell = row.createCell(i);
                    cell.setCellValue(header[i]);
                }
            }
            //处理数据集
            for (int i=0; i<list.size(); i++) {
                Row row = sheet.createRow(i+1);
                writeRowDataToSheet(list.get(i), row, clas);
            }
            //流数据写出
            workbook.write(out);
        }catch (Exception e){
            log.error("write data to file failed ...", e);
        }finally {
            try {
                if (workbook != null) {
                    workbook.close();
                }
            }catch (Exception e){
                log.error("close work book failed.", e);
            }
        }
    }


    /**
     * 单行数据写入
     * @param obj
     * @param row
     * @param clas
     * @param <T>
     */
    private <T> void writeRowDataToSheet(T obj, Row row, Class<T> clas) throws IllegalAccessException {
        Field[] fields = clas.getDeclaredFields();
        for (Field field : fields) {
            Index index = field.getAnnotation(Index.class);
            Cell cell = row.createCell(index.index() - 1);
            cell.setCellValue(field.get(obj).toString());
        }
    }


    /**
     * 解析csv文件(分段读)
     * @param file
     * @param claz
     * @param <T>
     * @return
     */
    public <T> List<T> readEntityFromFileWithBatch(File file, Class<T> claz, int start, int batchSize, int sheetIndex){
        List<T> list = null;
        try {
            list = new ArrayList<>();
            //先获取工作簿
            Workbook workbook = getWorkbook(file);
            //获取Sheet页面(默认获取第一个)
            Sheet sheet = workbook.getSheetAt(sheetIndex);
            //开始获取每行数据
            int size = batchSize > 0 ? batchSize : DEFAULT_BATCH_SIZE;
            //循环获取行数据
            for (int i = start; i < batchSize; i++){
                Row row = sheet.getRow(i);
                //如果下一行为空，跳出循环
                if (row == null){
                    break;
                }
                T data = conveterLineData(row, claz);
                if (data != null){
                    list.add(data);
                }
            }
        }catch (Exception e){
            list = Collections.emptyList();
            log.error("conveter data filed", e);
        }
        return list;
    }


    /**
     * 解析csv文件(分段读)
     * @param file
     * @param claz
     * @param <T>
     * @return
     */
    public <T> List<T> readEntityFromFile(File file, Class<T> claz){
        return readEntityFromFile(file, claz, DEFAULT_SHEET_NUMBER);
    }

    /**
     * 解析csv文件(分段读)
     * @param file
     * @param claz
     * @param <T>
     * @return
     */
    public <T> List<T> readEntityFromFile(File file, Class<T> claz, int sheetIndex){
        List<T> list = null;
        try {
            list = new ArrayList<>();
            //先获取工作簿
            Workbook workbook = getWorkbook(file);
            //获取Sheet页面(默认获取第一个)
            Sheet sheet = workbook.getSheetAt(sheetIndex);
            //开始获取每行数据
            int index = HEADER_LINE_NUMBER;
            //循环获取行数据
            while (true){
                Row row = sheet.getRow(index);
                //如果下一行为空，跳出循环
                if (row == null){
                    break;
                }
                T data = conveterLineData(row, claz);
                if (data != null){
                    list.add(data);
                }
                index++;
            }
        }catch (Exception e){
            list = Collections.emptyList();
            log.error("conveter data filed", e);
        }
        return list;
    }


    /**
     * 根据文件获取工作簿
     * @param file
     * @return
     */
    private Workbook getWorkbook(File file) throws IOException {
        String fileName = file.getName();
        Workbook workbook = null;
        if (fileName.endsWith(".xls")){
            workbook = new HSSFWorkbook(new FileInputStream(file));
        }else{
            workbook = new XSSFWorkbook(new FileInputStream(file));
        }
        return workbook;
    }


    /**
     * 转换一行数据
     * @param row
     * @param claz
     * @param <T>
     * @return
     */
    private <T> T conveterLineData(Row row, Class<T> claz) throws NoSuchMethodException, IllegalAccessException,
            InvocationTargetException, InstantiationException {
        //对象初始化
        T obj = claz.getDeclaredConstructor().newInstance();
        //获取当前实例的所有属性
        Field[] fields = claz.getDeclaredFields();
        //遍历属性，并设置数值
        for (Field field : fields) {
            Index index = field.getAnnotation(Index.class);
            if (index == null || index.ignore()) {
                continue;
            }
            Cell cell = row.getCell(index.index());
            //数据转换
            Object data = DataConveter.conveter(cell.getStringCellValue(), field.getType(), index.dateFormat());
            //设置数值
            field.setAccessible(true);
            field.set(obj, data);
        }
        return obj;
    }


}
